#include <board.h>
#include <board_memories.h>
#include <lcd/lcdd.h>
#include <lcd/draw.h>
#include <cp15/cp15.h>

////////////////////////////////////////////////////////////////////////////////
// CACHE

#define OFFSET_TABLE      0x8000

#if defined (AT91C_IRAM_1)
#define SRAM_ADDRESS  AT91C_IRAM_1
#define SRAM_SIZE     AT91C_IRAM_1_SIZE
#elif defined(AT91C_IRAM)
#define SRAM_ADDRESS  AT91C_IRAM
#define SRAM_SIZE     AT91C_IRAM_SIZE
#elif defined(AT91C_ISRAM)
#define SRAM_ADDRESS  AT91C_ISRAM
#define SRAM_SIZE     AT91C_ISRAM_SIZE
#else
#error SRAM define
#endif

#define SDRAM_ADDRESS 0x20000000

unsigned int *pTranslationTable = (unsigned int *) (SRAM_ADDRESS + OFFSET_TABLE);

static void InitMMU()
{
    int i;
    int addSDRAM;

    // Program the TTB
    _writeTTB((unsigned int) pTranslationTable);

    // Program the domain access register
    _writeDomain(0xC0000000); // domain 15: access are not checked

    // Reset table entries
    for (i = 0; i < 4096; i++) {
        pTranslationTable[i] = 0;
    }

    // Program level 1 page table entry
    // Vector adress
    for (i = 0; i < 0xB; ++i)
        pTranslationTable[i] = (i << 20) | // Physical Address
                (1 << 10) | // Access in supervisor mode
                (15 << 5) | // Domain
                (1 << 4) | (0 << 3) | // No D cache
                0x2; // Set as 1 Mbyte section

    pTranslationTable[SRAM_ADDRESS >> 20] = (SRAM_ADDRESS) | // Physical Address
                    (1 << 10) | // Access in supervisor mode
                    (15 << 5) | // Domain
                    (1 << 4) | (1 << 3) | // D cache
                    0x2; // Set as 1 Mbyte section

    // SDRAM adress (with D cache)
    for (addSDRAM = (SDRAM_ADDRESS >> 20); addSDRAM < (SDRAM_ADDRESS >> 20) + 64; ++addSDRAM)
        pTranslationTable[addSDRAM] = (addSDRAM << 20) | // Physical Address
                (1 << 10) | // Access in supervisor mode
                (15 << 5) | // Domain
                (1 << 4) | (1 << 3) | // D cache
                0x2; // Set as 1 Mbyte section

    // Peripherals adress
    pTranslationTable[0xFFF] = (0xFFF00000) | // Physical Address
            (1 << 10) | // Access in supervisor mode
            (15 << 5) | // Domain
            (1 << 4) | (0 << 3) | // No D cache
            0x2; // Set as 1 Mbyte section
}

////////////////////////////////////////////////////////////////////////////////
// TEST

static unsigned char _lcd_buffer [BOARD_LCD_WIDTH * BOARD_LCD_HEIGHT * 3];

int main() {
    int i;

    InitMMU();
    CP15_EnableMMU();
    CP15_Enable_D_Cache();
    CP15_Enable_I_Cache();

    // Initialize LCD
    LCDD_Fill(_lcd_buffer, COLOR_WHITE);
    LCDD_Initialize();
    LCDD_DisplayBuffer(_lcd_buffer);

#if !defined(sdram)
    BOARD_ConfigureSdram(BOARD_SDRAM_BUSWIDTH);
#endif

    while (1) {
        // Gay Proud
        LCDD_DrawRectangle(_lcd_buffer, 13 + 42*0, 0, 42, BOARD_LCD_HEIGHT, COLOR_RED);
        LCDD_DrawRectangle(_lcd_buffer, 13 + 42*1, 0, 42, BOARD_LCD_HEIGHT, COLOR_ORANGE);
        LCDD_DrawRectangle(_lcd_buffer, 13 + 42*2, 0, 42, BOARD_LCD_HEIGHT, COLOR_YELLOW);
        LCDD_DrawRectangle(_lcd_buffer, 13 + 42*3, 0, 42, BOARD_LCD_HEIGHT, COLOR_GREEN);
        LCDD_DrawRectangle(_lcd_buffer, 13 + 42*4, 0, 42, BOARD_LCD_HEIGHT, COLOR_BLUE);
        LCDD_DrawRectangle(_lcd_buffer, 13 + 42*5, 0, 42, BOARD_LCD_HEIGHT, COLOR_INDIGO);
        LCDD_DrawRectangle(_lcd_buffer, 13 + 42*6, 0, 42, BOARD_LCD_HEIGHT, COLOR_VIOLET);

        // Macho Man
        LCDD_DrawCircle(_lcd_buffer, 70, 50, 42, COLOR_OLIVE);
        LCDD_DrawHorizontalLine(_lcd_buffer, 0, 50, BOARD_LCD_WIDTH, COLOR_SIENNA);
        LCDD_DrawVerticalLine(_lcd_buffer, 70, 0, BOARD_LCD_HEIGHT, COLOR_SIENNA);
        LCDD_DrawTriangle(_lcd_buffer, 300, 150, 100, 70, 30, 200, COLOR_AZUR);

        // Pop Culture
        LCDD_DrawString(_lcd_buffer, 20, BOARD_LCD_HEIGHT - 25, "the cake is a lie", COLOR_SKYBLUE);

        // Kill them all...
        LCDD_Fill(_lcd_buffer, COLOR_WHITE);
    }

    return 0;
}
